home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / programr / drdobbs.zip / HELPDUMP.C < prev    next >
Text File  |  1993-08-02  |  41KB  |  1,233 lines

  1. /***************************************************
  2.   HLPDUMP1.C
  3.   Peter Davis - 05/93
  4.  
  5.   Taken from the original HELPDUMP.C written by
  6. Ron Burk.
  7.  
  8.   Dumps known files from WinHelp internal file
  9. system in a formatted output. If WHIFS file is
  10. unknown, hex/ascii dump of file is taken.
  11.  
  12.   HELPDUMP.C will replace HLPDUMP1.C with extended
  13. functionality for handling more WHIFS files.
  14.  
  15. ****************************************************/
  16.  
  17.  
  18. /*************************************************
  19.    If it's not Turbo C, assume MSC 6.0 As for the
  20.    rest of you, you'll have to customize. Sorry.
  21. **************************************************/
  22.  
  23. #pragma pack(1)   /* Make sure we get byte alignment */
  24.  
  25. #ifndef __TURBOC__
  26.   #include <graph.h>
  27.   #define clrscr() _clearscreen(_GCLEARSCREEN);
  28. #endif
  29.  
  30. #include <time.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <conio.h>
  35. #include <ctype.h>
  36. #include <limits.h>
  37. #include "whstruct.h"
  38. #include "helpdump.h"
  39.  
  40. HELPHEADER        HelpHeader;             /* Header for Help file.         */
  41. WHIFSBTREEHEADER  WHIFSHeader;            /* WHIFS Header record           */
  42. int               WHIFSLeafOne = -1;      /* First WHIFS Leaf Node         */
  43. long              FirstPageLoc;           /* Used by macros for b-trees    */
  44. char              WHIFSFileToRead[19];    /* Internal file to dump         */
  45. int               ReadWHIFSFile;          /* Flag to dump an internal file */
  46. SYSTEMHEADER      SysHeader;              /* Global System Header Record   */
  47. char              *PhrasesPtr; 
  48. int               TopicUse;
  49.  
  50.  
  51. /***************************************************
  52.   Finds the first leaf in the WHIFS B-Tree
  53. ****************************************************/
  54. void WHIFSGetFirstLeaf(FILE *HelpFile) {
  55.     
  56.     int               CurrLevel = 1;  /* Current Level in B-Tree */
  57.     BTREEINDEXHEADER  CurrNode;       /* Current Node in B-Tree  */
  58.     int               NextPage = 0;   /* Next Page to go to      */
  59.  
  60.     /* Go to the beginning of WHIFS B-Tree */
  61.     fseek(HelpFile, HelpHeader.WHIFS, SEEK_SET);
  62.     fread(&WHIFSHeader, sizeof(WHIFSHeader), 1, HelpFile);
  63.  
  64.     FirstPageLoc = HelpHeader.WHIFS + sizeof(WHIFSHeader);
  65.  
  66.     /* Find First Leaf */
  67.     while (CurrLevel < WHIFSHeader.NLevels) {
  68.        fread(&CurrNode, sizeof(CurrNode), 1, HelpFile);
  69.  
  70.        /* Next Page is conveniently the first byte of the page */
  71.        fread(&NextPage, sizeof(int), 1, HelpFile);
  72.        GotoWHIFSPage(NextPage);
  73.        CurrLevel++;
  74.     }
  75.  
  76.     /* First Leaf page is here */
  77.     WHIFSLeafOne = NextPage;
  78. }
  79.        
  80.     
  81.  
  82.  
  83.  
  84. /***************************************************
  85.   Gets a particular file by file number.
  86.   Needs the root node of the tree.
  87.   Returns the offset of the file and the filename.
  88. ****************************************************/
  89.  
  90. void GetFile(FILE *HelpFile, int FileNumber, long *FileOffset, char *FileName) {
  91.  
  92.     BTREENODEHEADER CurrentNode;      
  93.     int             CurrPage;              /* Keep track of page # */
  94.     int             Index, counter = 0;
  95.     char            c, TempFile[19];
  96.  
  97.     
  98.     /* Skip pages we don't need */
  99.     CurrentNode.NextPage = WHIFSLeafOne;
  100.     do {
  101.         CurrPage = CurrentNode.NextPage;
  102.         GotoWHIFSPage(CurrPage);
  103.         fread(&CurrentNode, sizeof(CurrentNode), 1, HelpFile);
  104.         counter += CurrentNode.NEntries;
  105.     } while (counter < FileNumber);
  106.  
  107.     for (counter -= CurrentNode.NEntries; counter <= FileNumber; counter++) {
  108.         Index = 0;
  109.     while(c = fgetc(HelpFile))
  110.             TempFile[Index++] = c;
  111.          
  112.         TempFile[Index] = 0;  /* End of line */
  113.         fread(FileOffset, sizeof(long), 1, HelpFile);
  114.     }
  115.     strcpy(FileName, TempFile);
  116.  
  117. }
  118.  
  119.  
  120. /***************************************************
  121.   Loads the SysHeader into memory. Need this later
  122. on to determine if compression is used on help file.
  123. ****************************************************/
  124.  
  125. void SysLoad(FILE *HelpFile, long FileStart) {
  126.  
  127.    FILEHEADER      FileHdr;
  128.  
  129.    fseek(HelpFile, FileStart, SEEK_SET);
  130.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  131.    fread(&SysHeader, sizeof(SysHeader), 1, HelpFile);
  132.  
  133. }
  134.  
  135.  
  136. /*************************************************
  137.   Returns a 1 if the bit is set, else 0 returned
  138. **************************************************/
  139.  
  140. int BitSet(BYTE BitMap, int Bit) {
  141.   
  142.    if (BitMap & (1<<Bit)) return 1;
  143.                      else return 0;
  144.  
  145. }
  146.  
  147.  
  148. /*************************************************
  149.   Decides how many bytes to read, depending on the
  150.   number of bits set in the Bitmap
  151. **************************************************/
  152.  
  153. int BytesToRead(BYTE BitMap) {
  154.  
  155. int TempSum, counter;
  156.  
  157.     TempSum = 8;
  158.     for (counter = 0; counter < 8; counter ++)
  159.        TempSum += BitSet(BitMap, counter);
  160.  
  161.     return TempSum;
  162. }
  163.  
  164.  
  165. /*************************************************
  166.   Decompresses the data using Microsoft's LZ77
  167. derivative.
  168. **************************************************/ 
  169.  
  170. WORD Decompress(FILE *HelpFile, WORD CompSize, char *Buffer) {
  171.  
  172. WORD InBytes = 0;        /* How many bytes read in                    */
  173. WORD OutBytes = 0;       /* How many bytes written out                */
  174. BYTE BitMap, Set[16];    /* Bitmap and bytes associated with it       */
  175. int  NumToRead;          /* Number of bytes to read for next group    */
  176. int  counter, Index;     /* Going through next 8-16 codes or chars    */
  177. int  Length, Distance;   /* Code length and distance back in 'window' */
  178. char *CurrPos;           /* Where we are at any given moment          */
  179. char *CodePtr;           /* Pointer to back-up in LZ77 'window'       */
  180.  
  181.  
  182.    CurrPos = Buffer;
  183.  
  184.    /* Go through until we're done */
  185.    while (InBytes < CompSize) {
  186.  
  187.       /* Get BitMap and data following it */
  188.       BitMap = fgetc(HelpFile);
  189.       NumToRead = BytesToRead(BitMap);
  190.  
  191.       /* If we're trying to read more than we've got left, only read what we have left. */
  192.       NumToRead = (CompSize - InBytes) < NumToRead ? CompSize-InBytes : NumToRead;
  193.  
  194.       fread(Set, 1, NumToRead, HelpFile);    
  195.       InBytes += NumToRead + 1;
  196.  
  197.       /* Go through and decode data */
  198.       for (counter = 0, Index = 0; counter < 8; counter++) {
  199.  
  200.          /* It's a code, so decode it and copy the data */
  201.          if (BitSet(BitMap, counter)) {
  202.             Length = ((Set[Index+1] & 0xF0) >> 4) + 3;
  203.             Distance = (256 * (Set[Index+1] & 0x0F)) + Set[Index] + 1;
  204.  
  205.             CodePtr = CurrPos - Distance;
  206.  
  207.             /* Copy data from 'window' */
  208.             while (Length) {
  209.                *CurrPos++ = *CodePtr++;
  210.                OutBytes++;
  211.                Length--;
  212.             } 
  213.  
  214.             Index += 2;
  215.  
  216.          } /* if */
  217.  
  218.          else {
  219.             *CurrPos++ = Set[Index++];
  220.             OutBytes++;
  221.          }
  222.  
  223.       } /* for */
  224.  
  225.    } /* while  */
  226.  
  227.    return OutBytes;
  228.    
  229.  
  230. /***************************************************
  231.   List the WHIFS directory to the screen.
  232. ****************************************************/
  233.  
  234. void ListFiles(FILE *HelpFile) {
  235.  
  236.     DWORD           FileCounter;
  237.     char            FileName[20];
  238.     long            FileOffset;
  239.  
  240.  
  241.     /* Load WHIFS Header and Get first leaf */
  242.     WHIFSGetFirstLeaf(HelpFile);
  243.  
  244.     for (FileCounter = 0;
  245.          FileCounter < WHIFSHeader.TotalWHIFSEntries;
  246.          FileCounter++) {
  247.  
  248.         GetFile(HelpFile, FileCounter, &FileOffset, FileName);
  249.  
  250.         printf("File:%-10s FileOffset:0x%08lX ",FileName, FileOffset);
  251.  
  252.         /* Make it double column */
  253.         if ((FileCounter % 2) != 0) printf("\n");
  254.              else printf("|");
  255.     }
  256.  
  257. }
  258.  
  259.  
  260. /***************************************************
  261.   Performs a Hex/ASCII dump of a WHIFS file.
  262. ****************************************************/
  263.  
  264. void HexDump(FILE *HelpFile, long FileStart) {
  265.  
  266.    FILEHEADER  FileHdr;
  267.    char        Buffer[16];
  268.    long        counter;
  269.    int         BytesToPrint, Index;
  270.  
  271.  
  272.    fseek(HelpFile, FileStart, SEEK_SET);
  273.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  274.  
  275.    printf("File Size: 0x%08lX\n\n", FileHdr.FileSize);
  276.  
  277.    printf("Offset                   Hex Values                           Ascii\n");
  278.    printf("-------------------------------------------------------------------------\n");
  279.  
  280.    for (counter = 0; counter < FileHdr.FileSize; counter+=16) {
  281.  
  282.       printf("0x%08lX: ", counter);
  283.  
  284.       /* If this is the last line, how many bytes are in it? */
  285.       BytesToPrint = ((FileHdr.FileSize - counter) > 16) ? 16 : FileHdr.FileSize - counter;
  286.       fread(Buffer, BytesToPrint, 1, HelpFile);
  287.  
  288.       /* Dump Hex */
  289.       for (Index=0; Index < BytesToPrint; Index++)
  290.           printf("%02X ", (BYTE) Buffer[Index]);
  291.  
  292.       /* If last line, fill in blanks */
  293.       for (Index=0; Index < 16-BytesToPrint; Index++) 
  294.           printf("   ");
  295.  
  296.       /* Dump Ascii */
  297.       for (Index=0; Index < BytesToPrint; Index++) 
  298.           putchar( isprint( Buffer[Index] ) ? Buffer[Index] : '.' );
  299.  
  300.       putchar('\n');
  301.    }
  302.  
  303.    free(Buffer);
  304. }
  305.  
  306.  
  307.  
  308.  
  309. /***************************************************
  310.   Display information about the |SYSTEM file.
  311. ****************************************************/
  312.  
  313. void SysDump(FILE *HelpFile, long FileStart) {
  314.  
  315.    FILEHEADER      FileHdr;
  316.    char            HelpFileTitle[33];
  317.    SYSTEMREC       SystemRec;
  318.    WORD            CurrentLocation;
  319.    struct tm       *TimeRec;
  320.    SECWINDOW       *SWin;     /* Secondary Window record */
  321.  
  322.    fseek(HelpFile, FileStart, SEEK_SET);
  323.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  324.    fread(&SysHeader, sizeof(SysHeader), 1, HelpFile);
  325.    printf("|SYSTEM Dump\n\n\n");
  326.  
  327.    /* Figure out Version and Revision */
  328.    if (SysHeader.Revision == 0x0F) printf("HC.EXE  3.00 Help Compiler used\n");
  329.    else if (SysHeader.Revision == 0x15) printf("HC.EXE  3.10 Help Compiler used\n");
  330.    else if (SysHeader.Revision == 0x21) printf("MVC.EXE  Multimedia Compiler used.\n");
  331.    else printf("Unknown Compiler used!\n");
  332.  
  333.    printf("\nVersion: %d\nRevision: %d\n", SysHeader.Version, SysHeader.Revision);
  334.    printf("Flag: 0x%04X  - ",SysHeader.Flags);
  335.  
  336.    /* Determine compression, if any. */
  337.    if (SysHeader.Flags == 0x00) printf("No compression\n");
  338.    else if (SysHeader.Flags & COMPRESSION_310) printf("Compressed\n");
  339.    else if (SysHeader.Flags & COMPRESSION_UNKN) printf("Compressed\n");
  340.    else printf("Unknown\n");
  341.  
  342.    TimeRec=localtime(&SysHeader.GenDate);
  343.    printf("Help File Generated: %s", asctime(TimeRec));
  344.  
  345.    /* If 3.0 get title */
  346.    CurrentLocation=12;
  347.    if (SysHeader.Revision == 0x0F) {
  348.       fgets(HelpFileTitle, 33, HelpFile);
  349.       printf("Help File Title: %s\n", HelpFileTitle);
  350.    }
  351.  
  352.    /* Else, get 3.1 System records */
  353.    else {
  354.       while (CurrentLocation < FileHdr.FileSize) {
  355.  
  356.          /* Read in system record and SystemRec data */
  357.          fread(&SystemRec, 4, 1, HelpFile);
  358.          SystemRec.RData = malloc(SystemRec.DataSize);
  359.          fread(SystemRec.RData, SystemRec.DataSize, 1, HelpFile);
  360.          CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  361.  
  362.          switch(SystemRec.RecordType) {
  363.              case 0x0001:  printf("Help File Title: %s\n", SystemRec.RData);
  364.                    break;
  365.  
  366.              case 0x0002:  printf("Copyright Notice: %s\n", SystemRec.RData);
  367.                    break;
  368.  
  369.              case 0x0003:  printf("Contents ID: 0x%04X\n", (long) *SystemRec.RData);
  370.                    break;
  371.  
  372.              case 0x0004:  printf("Macro Data: %s\n",SystemRec.RData);
  373.                    break;
  374.  
  375.              case 0x0005:  printf("Icon in System record\n");
  376.                    break;
  377.  
  378.              case 0x0006:  printf("\nSecondary window:\n");
  379.                            SWin = (SECWINDOW *)SystemRec.RData;
  380.                            printf("Flag: %d\n", SWin->Flags);
  381.                            if (SWin->Flags & WSYSFLAG_TYPE) 
  382.                               printf("Type: %s\n", SWin->Type);
  383.                            if (SWin->Flags & WSYSFLAG_NAME) 
  384.                               printf("Name: %s\n", SWin->Name);
  385.                            if (SWin->Flags & WSYSFLAG_CAPTION) 
  386.                               printf("Caption: %s\n", SWin->Caption);
  387.                            if (SWin->Flags & WSYSFLAG_X) 
  388.                               printf("X: %d\n", SWin->X);
  389.                            if (SWin->Flags & WSYSFLAG_Y) 
  390.                               printf("Y: %d\n", SWin->Y);
  391.                            if (SWin->Flags & WSYSFLAG_WIDTH)
  392.                               printf("Width: %d\n", SWin->Width);
  393.                            if (SWin->Flags & WSYSFLAG_HEIGHT)
  394.                               printf("Height: %d\n", SWin->Height);
  395.                            if (SWin->Flags & WSYSFLAG_MAXIMIZE) 
  396.                               printf("Maximize Flag: %d\n", SWin->Maximize);
  397.                            if (SWin->Flags & WSYSFLAG_RGB) 
  398.                               printf("RGB Foreground Colors Set\n");
  399.                            if (SWin->Flags & WSYSFLAG_RGBNSR)
  400.                               printf("RGB For Non-Scrollable Region Set\n");
  401.                            if (SWin->Flags & WSYSFLAG_TOP) 
  402.                               printf("Secondary Window is always On Top\n");
  403.                    break;
  404.  
  405.              case 0x0008:  printf("Citation: %s\n", SystemRec.RData);
  406.                    break;
  407.  
  408.              default:      printf("Unknown record type: 0x%04X\n",SystemRec.RecordType);
  409.  
  410.          } /* switch */
  411.  
  412.          free(SystemRec.RData);
  413.  
  414.       } /* while */
  415.    } /* else */
  416. } /* SysDump */
  417.  
  418.  
  419. /***************************************************
  420.   Prints a Phrase from the Phrase table.
  421. ****************************************************/
  422. void PrintPhrase(char *Phrases, int PhraseNum) {
  423.  
  424.     int  *Offsets;
  425.     char *p;
  426.  
  427.     Offsets = (int *)Phrases;
  428.  
  429.     p = Phrases+Offsets[PhraseNum];
  430.     while (p < Phrases + Offsets[PhraseNum + 1])
  431.        putchar(*p++);
  432.  
  433.  
  434. }
  435.  
  436. /***************************************************
  437.    List all of the phrases if they're uncompressed.
  438.    If they're compressed, dump the hex/ascii.
  439. ****************************************************/
  440.  
  441. void PhraseDump(FILE *HelpFile, long FileStart) {
  442.  
  443.    FILEHEADER      FileHdr;
  444.    PHRASEHDR       PhraseHdr;
  445.    int             *Offsets, counter;
  446.    char            *Phrases;
  447.    WORD            DeCompSize;
  448.  
  449.    /* Go to the phrases file and get the headers */
  450.    fseek(HelpFile, FileStart, SEEK_SET);
  451.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  452.    fread(&PhraseHdr, sizeof(PhraseHdr), 1, HelpFile);
  453.  
  454.  
  455.    if (!TopicUse) printf("Phrase#  -  Phrase\n");
  456.  
  457.  
  458.    /* Allocate space and decompress if it's compressed, else read in. */
  459.    if ((SysHeader.Flags & COMPRESSION_310) || (SysHeader.Flags & COMPRESSION_UNKN)) {
  460.       if ((Offsets = malloc(PhraseHdr.PhrasesSize + (PhraseHdr.NumPhrases + 1) * 2)) == NULL) {
  461.          printf("No room to decompress |Phrases.");
  462.          return;
  463.       }
  464.       Phrases = Offsets + fread(Offsets, 2, PhraseHdr.NumPhrases+1, HelpFile);
  465.       DeCompSize = Decompress(HelpFile,
  466.                               FileHdr.FileSize - (sizeof(PhraseHdr) + 2 * (PhraseHdr.NumPhrases+1)),
  467.                               Phrases);
  468.       if (DeCompSize != PhraseHdr.PhrasesSize) {
  469.          printf("The amount of data after decompression does not match\n");
  470.          printf("the expected size from the Phrase Header!!!\n\n");
  471.       }
  472.  
  473.    } /* if */
  474.  
  475.      /* Else, no compression, so just read in the data */
  476.    else {
  477.       if ((Offsets = malloc(FileHdr.FileSize - sizeof(PhraseHdr))) == NULL) {
  478.          printf("No room to decompress |Phrases.");     
  479.          return;
  480.       }
  481.       /* Backup 4 bytes for uncompressed Phrases (no PhrasesSize) */
  482.       fseek(HelpFile, -4, SEEK_CUR);
  483.       fread(Offsets, FileHdr.FileSize - 4, 1, HelpFile);
  484.    } /* else */
  485.  
  486.    Phrases = (char *) Offsets;
  487.    PhrasesPtr = Phrases;
  488.    if (TopicUse) return;
  489.  
  490.    for (counter=0; counter < PhraseHdr.NumPhrases; counter++) {
  491.       printf("\n%d    -  ", counter);
  492.       PrintPhrase(Phrases, counter);
  493.    }
  494.    free(Offsets);
  495. }
  496.  
  497.  
  498. /***************************************************
  499.    Lists all of the fonts and font descriptors.
  500.    Fonts are fixed length 20 followed by Font
  501.    descriptors of Fixed Length 11 bytes.
  502. ****************************************************/
  503.  
  504. void FontDump(FILE *HelpFile, long FileStart) {
  505.  
  506.    FILEHEADER      FileHdr;
  507.    FONTHEADER      FontHdr;
  508.    FONTDESCRIPTOR  FontDesc;
  509.    char            AFont[20];
  510.    long            FontStart, CurrLoc;
  511.    int             counter;
  512.  
  513.    /* Go to the FONT file and get the headers */
  514.    fseek(HelpFile, FileStart, SEEK_SET);
  515.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  516.    fread(&FontHdr, sizeof(FontHdr), 1, HelpFile);
  517.  
  518.    printf("|FONTS\n\n Number Fonts: %d\n",FontHdr.NumFonts);
  519.    printf("Font #  -  Font Name\n");
  520.  
  521.    /* Keep track of start of fonts */
  522.    FontStart = ftell(HelpFile);
  523.    for (counter = 0; counter < FontHdr.NumFonts; counter++) {
  524.        fread(AFont, 20, 1, HelpFile);
  525.        printf(" %3d    -  %s\n", counter, AFont);
  526.    }
  527.  
  528.    /* Go to Font Descriptors. Don't actually need this, because we're
  529.       there, but wanted to show how to get there using the offset.    */
  530.    fseek(HelpFile, FontStart + (long)(FontHdr.DescriptorsOffset) - sizeof(FontHdr), SEEK_SET); 
  531.    printf("\nNum Font Descriptors: %d\n", FontHdr.NumDescriptors);
  532.    printf("Default Descriptor: %d\n\n", FontHdr.DefDescriptor);
  533.    printf("Attributes: n=none  b=bold  i=ital  u=undr  s=strkout  d=dblundr  C=smallcaps\n\n");
  534.    printf("Font Name            PointSize  Family   FG RGB      BG RGB     Attributes\n");
  535.    printf("--------------------------------------------------------------------------\n");
  536.  
  537.    for (counter = 0; counter < FontHdr.NumDescriptors; counter++) {
  538.       fread(&FontDesc, sizeof(FontDesc), 1, HelpFile);
  539.       CurrLoc = ftell(HelpFile);
  540.       fseek(HelpFile, FontStart + (20L * FontDesc.FontName), SEEK_SET);
  541.       fread(AFont, 20, 1, HelpFile);
  542.       fseek(HelpFile, CurrLoc, SEEK_SET);
  543.       
  544.       /* write out info on Font descriptor */
  545.       printf("%-20s    %4.1f    ", AFont, (float)(FontDesc.HalfPoints / 2));
  546.       switch (FontDesc.FontFamily) {
  547.          case FAM_MODERN: printf("Modern");
  548.                           break;
  549.  
  550.          case FAM_ROMAN:  printf("Roman ");
  551.                           break;
  552.  
  553.          case FAM_SWISS:  printf("Swiss ");
  554.                           break;
  555.  
  556.          case FAM_SCRIPT: printf("Script");
  557.                           break;
  558.  
  559.          case FAM_DECOR:  printf("Decor ");
  560.                           break;
  561.  
  562.          default:         printf("0X%02X ", FontDesc.FontFamily);
  563.                           break;
  564.       } /* Switch */
  565.       printf(" 0X%08lX  ",RGB(FontDesc.FGRGB[0], FontDesc.FGRGB[1], FontDesc.FGRGB[2]));
  566.       printf("0X%08lX    ",RGB(FontDesc.BGRGB[0], FontDesc.BGRGB[1], FontDesc.BGRGB[2]));
  567.  
  568.       if (FontDesc.Attributes == 0) putchar('n');
  569.       if (FontDesc.Attributes & FONT_BOLD) putchar('b');
  570.       if (FontDesc.Attributes & FONT_ITAL) putchar('i');
  571.       if (FontDesc.Attributes & FONT_UNDR) putchar('u');
  572.       if (FontDesc.Attributes & FONT_STRK) putchar('s');
  573.       if (FontDesc.Attributes & FONT_DBUN) putchar('d');
  574.       if (FontDesc.Attributes & FONT_SMCP) putchar('C');
  575.       printf("\n");
  576.    }
  577. }
  578.  
  579.  
  580.  
  581. /***************************************************
  582.    Dumps the |TOMAP file. Simple list of topic
  583.    numbers and topic offsets.
  584. ****************************************************/
  585. void ToMapDump(FILE *HelpFile, long FileStart) {
  586.  
  587.    FILEHEADER   FileHdr;
  588.    TOMAPHEADER  ToMapHdr;
  589.    int          counter;
  590.  
  591.    /* Go to the TOMAP file and get the headers */
  592.    fseek(HelpFile, FileStart, SEEK_SET);
  593.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  594.    fread(&ToMapHdr, 64, 1, HelpFile);
  595.  
  596.    ToMapHdr.ToMapLen = (FileHdr.FileSize - (16 * 4)) / 4;
  597.    ToMapHdr.TopicPtr = malloc(ToMapHdr.ToMapLen);
  598.    fread(ToMapHdr.TopicPtr, ToMapHdr.ToMapLen, 1, HelpFile);
  599.  
  600.    printf("Topic #  -  Topic Start\n");
  601.    for (counter=0; counter < ToMapHdr.ToMapLen; counter++)
  602.        printf("%3d  -  0x%08lX\n", counter, ToMapHdr.TopicPtr[counter]);
  603.    free(ToMapHdr.TopicPtr);
  604. }
  605.  
  606.  
  607. /***************************************************
  608.    Because the topic file is broken into 4k blocks,
  609.    we'll have to handle all the reads. The main
  610.    idea is to filter out the TOPICBLOCKHEADERs and
  611.    do an decompression that needs doing.
  612. ****************************************************/
  613. long TopicRead(BYTE *Dest, long NumBytes, FILE *HelpFile) {
  614.  
  615.    static long        CurrBlockLoc = 0;   /* Where we are in the block  */
  616.    static BYTE        *DCmpBlock = NULL;  /* Block of uncompressed data */
  617.    static long        DecompSize;         /* Size of block after decomp */
  618.    static long        TopicStart, BlkNum; /* Start of |TOPIC file       */
  619.    int                BytesLeft;          /* # Bytes left to return     */
  620.    TOPICBLOCKHEADER   BlockHeader;
  621.    TOPICLINK          *TempLink;
  622.    long               EndOffset;
  623.  
  624.    /* If NumBytes = 0, then we're done and need to free memory */
  625.    if (NumBytes == -1) {
  626.      free(DCmpBlock);
  627.      return 0;
  628.    }
  629.  
  630.    /* Is this our first time in? If so, allocate space needed */
  631.    if (!DCmpBlock) {
  632.       if ((SysHeader.Flags & COMPRESSION_310) || (SysHeader.Flags & COMPRESSION_UNKN)) {
  633.      /* MS assumes up to 75% compression. That's a bit */
  634.      /* optimistic given the algorithm. Expect more    */
  635.      /* like 25%-30% at best. We'll go with 75% anyway */
  636.          DCmpBlock = malloc(4 * TopicBlockSize);
  637.  
  638.          /* Get the current Topic location */
  639.          TopicStart = ftell(HelpFile);
  640.   
  641.          /* We're at Block 0 */
  642.          BlkNum = 0;
  643.  
  644.          DecompSize = 0;   /* Set initial size to 0 */
  645.          if (!DCmpBlock) {
  646.              printf("Not enough memory to decompress |TOPIC file!\n");
  647.              return -1;
  648.          }
  649.  
  650.       }
  651.       else {
  652.  
  653.          DCmpBlock = malloc(TopicBlockSize);  /* Space needed for decompressed block */
  654.  
  655.          DecompSize = 0;  /* Set initial size to zero */
  656.          if (!DCmpBlock) {
  657.              printf("Not enough memory to handle a |TOPIC file!\n");
  658.              return -1;
  659.          }
  660.       }
  661.       /* Don't really need the first block header, so get it out of the way */
  662.       fread(&BlockHeader, sizeof(BlockHeader), 1, HelpFile);
  663.    }
  664.  
  665.    /* Get the info for the TopicRead */
  666.    BytesLeft = NumBytes;
  667.  
  668.    while (BytesLeft) {
  669.  
  670.       /* Do we need to read in a new block? */
  671.       if (DecompSize == CurrBlockLoc) {
  672.  
  673.       /* Increment the block number */
  674.          BlkNum++;
  675.          /* If it's a compressed block, decompress it */
  676.          if ((SysHeader.Flags & COMPRESSION_310) || (SysHeader.Flags & COMPRESSION_UNKN)) {
  677.                 DecompSize = Decompress(HelpFile, TopicBlockSize-1, DCmpBlock);
  678.             /* Align ourselves at next 4k block */
  679.             fseek(HelpFile, TopicStart + (4096L * BlkNum), SEEK_SET);
  680.          }
  681.          else
  682.             DecompSize = fread(DCmpBlock, 1, TopicBlockSize, HelpFile);
  683.  
  684.          CurrBlockLoc = 0;
  685.  
  686.  
  687.          /* This is the tricky part! If the block isn't full, */
  688.          /* we need to find the end of it and change          */
  689.          /* DecompSize to adjust to the real end. We do this  */
  690.          /* by reading the block header of the next block     */
  691.          /* and going to the last record in this block.       */
  692.          fread(&BlockHeader, sizeof(BlockHeader), 1, HelpFile);
  693.  
  694.          /* Get offset of last topic link. (Don't need the block #, hence 3FFFh) */
  695.          EndOffset = BlockHeader.LastTopicLink & 0x3FFF;
  696.          TempLink = (TOPICLINK *) (DCmpBlock + EndOffset - sizeof(BlockHeader) );
  697.  
  698.          /* Actual end of the data (Don't include header) */
  699.          EndOffset += (TempLink->BlockSize - sizeof(BlockHeader));
  700.  
  701.          /* If end is shorter than topic block, use it, otherwise topic block is full */
  702.          if (EndOffset > DecompSize) {
  703.              /* Adjust DecompSize if crossing 4k boundary */
  704.              EndOffset = TempLink->BlockSize - ((TempLink->NextBlock) & 0x3FFF);
  705.              DecompSize = (BlockHeader.LastTopicLink & 0x3FFF) + EndOffset;
  706.          }
  707.          else DecompSize = EndOffset;
  708.  
  709.      } /* If */
  710.  
  711.      *(Dest++) = *(DCmpBlock + (CurrBlockLoc++) );
  712.       BytesLeft--;
  713.    } /* While (BytesLeft) */
  714.    return NumBytes;
  715.  
  716. }
  717.  
  718.  
  719. /***************************************************
  720.    Displays a string from a topic link record. Checks
  721. for Phrase replacement and non-printable chars.
  722. ****************************************************/
  723.  
  724. void StringPrint(char *String, long Length) {
  725.  
  726.    BYTE            Byte1, Byte2;
  727.    int             CurChar, PhraseNum;
  728.    int             PostSpace;
  729.    long            counter;
  730.  
  731.    for(counter = 0; counter < Length; counter++) {
  732.       CurChar = * ((char *) (String + counter));
  733.  
  734.       /* Check for Phrase replacement! */
  735.       if ((CurChar > 0) && (CurChar < 10)) {
  736.          Byte1 = CurChar;
  737.          counter++;
  738.          CurChar = * ((char *) (String + counter));
  739.          Byte2 = CurChar;
  740.          PhraseNum = (256 * (Byte1 - 1) + Byte2);
  741.  
  742.          /* If there's a remainder, we have a space after the phrase */
  743.          PostSpace = PhraseNum % 2;
  744.          /* Divide Phrase number by 2 */
  745.          PhraseNum = PhraseNum / 2;
  746.          PrintPhrase(PhrasesPtr, PhraseNum);
  747.          if (PostSpace) putchar(' ');
  748.       }
  749.       else 
  750.          if (isprint(CurChar)) putchar(CurChar);
  751.             else printf("(0x%02X)", (CurChar & 0x00FF));
  752.    }
  753. }
  754. /***************************************************
  755.    List all of the phrases if they're uncompressed.
  756.    If they're compressed, dump the hex/ascii.
  757. ****************************************************/
  758.  
  759. void TopicDump(FILE *HelpFile, long FileStart) {
  760.  
  761.    FILEHEADER      FileHdr;
  762.    TOPICHEADER     *TopicHdr;
  763.    TOPICLINK       TopicLink;
  764.  
  765.    /* Go to the TOPIC file and get the headers */
  766.    fseek(HelpFile, FileStart, SEEK_SET);
  767.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  768.  
  769.  
  770.    do {
  771.       TopicRead((BYTE *) &TopicLink, sizeof(TopicLink) - 4, HelpFile);
  772.  
  773.       /* TopicLink.DataLen2 assumes you are doing Phrase replacement.  */
  774.       /* Since we're not, we don't know the length of the phrases that */
  775.       /* would be replaced. Therefore we're going to modify the value  */
  776.       /* of DataLen2 to reflect the value without phrase replacement.  */
  777.  
  778.       if ((SysHeader.Flags & COMPRESSION_310) || (SysHeader.Flags & COMPRESSION_UNKN))
  779.          TopicLink.DataLen2 = TopicLink.BlockSize - TopicLink.DataLen1;
  780.  
  781.  
  782.  
  783.       /* 21=sizeof(TopicLink) - LinkData1 & LinkData2 fields. */
  784.       TopicLink.LinkData1 = malloc(TopicLink.DataLen1 - 21);
  785.       if(!TopicLink.LinkData1) {
  786.           printf("Error allocating TopicLink.LinkData1!\n");
  787.           return;
  788.       }
  789.       TopicRead(TopicLink.LinkData1, TopicLink.DataLen1 - 21, HelpFile);
  790.       if (TopicLink.DataLen2 > 0) {
  791.           TopicLink.LinkData2 = malloc(TopicLink.DataLen2 + 1);
  792.           if(!TopicLink.LinkData2) {
  793.              printf("Error allocating TopicLink.LinkData2!\n");
  794.              return;
  795.           }
  796.           TopicRead(TopicLink.LinkData2, TopicLink.DataLen2, HelpFile);
  797.       }
  798.  
  799.  
  800.       /* Display a Topic Header record */
  801.       if (TopicLink.RecordType == TL_TOPICHDR) {
  802.          TopicHdr = (TOPICHEADER *)TopicLink.LinkData1;
  803.      printf("========================================================\n");
  804.          printf("Topic#: %ld     Block Size: %ld\n",
  805.                 TopicHdr->TopicNum,
  806.                 TopicHdr->BlockSize);
  807.  
  808.          if (TopicLink.DataLen2 > 0)
  809.             StringPrint(TopicLink.LinkData2, TopicLink.DataLen2);
  810.          else printf("\n");
  811.       }
  812.  
  813.       /* Show a 'text' type record. */
  814.       else if (TopicLink.RecordType == TL_DISPLAY) {
  815.          printf("--------------------------------------------------------\n");
  816.          printf("Block Size: %ld\n", TopicLink.BlockSize);
  817.          StringPrint(TopicLink.LinkData2, TopicLink.DataLen2);
  818.       }
  819.       printf("\n\n");
  820.       free(TopicLink.LinkData1);
  821.       if (TopicLink.DataLen2 > 0) free(TopicLink.LinkData2);
  822.    } while(TopicLink.NextBlock != -1);
  823. }
  824.  
  825. /***************************************************
  826.   Get First leaf. Used by TTLDump ,KWBTreeDump and
  827. ContextDump.
  828. ****************************************************/
  829. int OtherGetFirstLeaf(FILE *HelpFile, long FirstPageLoc, int LeafLevel) {
  830.  
  831.    int               CurrLevel = 1;
  832.    BTREEINDEXHEADER  CurrNode;
  833.    int               NextPage = 0;
  834.  
  835.  
  836.    while(CurrLevel < LeafLevel) {
  837.       fread(&CurrNode, 4, 1, HelpFile);
  838.  
  839.       fread(&NextPage, sizeof(int), 1, HelpFile);
  840.       GotoPage(NextPage);
  841.       CurrLevel++;
  842.    }
  843.  
  844.    return NextPage;
  845. }
  846.  
  847.  
  848. /***************************************************
  849.   Dumps the Topic Titles B-Tree
  850. ****************************************************/
  851.  
  852. void TTLDump(FILE *HelpFile, long FileStart) {
  853.  
  854.    FILEHEADER      FileHdr;
  855.    char            Title[80], c;
  856.    int             count, Index;
  857.    long            CurrPage, FirstPageLoc, TopicOffset;
  858.    BTREEHEADER     BTreeHdr;
  859.    BTREENODEHEADER CurrNode;
  860.  
  861.    /* Go to the TTLBTREE file and get the headers */
  862.    fseek(HelpFile, FileStart, SEEK_SET);
  863.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  864.    fread(&BTreeHdr, sizeof(BTreeHdr), 1, HelpFile);
  865.  
  866.    /* Save the current location */
  867.    FirstPageLoc = ftell(HelpFile);
  868.    GotoPage(BTreeHdr.RootPage);
  869.  
  870.    printf("# Titles in |TTLBTREE %lu\n\n", BTreeHdr.TotalBtreeEntries);
  871.    CurrPage = OtherGetFirstLeaf(HelpFile, FirstPageLoc, BTreeHdr.NLevels);
  872.  
  873.    do {
  874.        GotoPage(CurrPage);
  875.        fread(&CurrNode, 8, 1, HelpFile);
  876.        for(count = 1; count <= CurrNode.NEntries; count++) {
  877.  
  878.           fread(&TopicOffset, sizeof(TopicOffset), 1, HelpFile);
  879.  
  880.           Index = 0;
  881.       while(c = fgetc(HelpFile))
  882.              Title[Index++] = c;
  883.           Title[Index] = 0;
  884.  
  885.           printf("Topic Offset:0x%08lX  Title: %s\n", TopicOffset, Title);
  886.        }
  887.        CurrPage = CurrNode.NextPage;
  888.  
  889.    } while(CurrPage != -1);
  890. }
  891.  
  892.  
  893. /***************************************************
  894.   Dumps the Keyword Map file
  895. ****************************************************/
  896.  
  897. void KWMapDump(FILE *HelpFile, long FileStart) {
  898.  
  899.    FILEHEADER      FileHdr;
  900.    WORD            NumKWMaps, count;
  901.    KWMAPREC        KeywordMap;
  902.  
  903.    /* Go to the KWMAP file and get the headers */
  904.    fseek(HelpFile, FileStart, SEEK_SET);
  905.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  906.  
  907.    fread(&NumKWMaps, sizeof(NumKWMaps), 1, HelpFile);
  908.  
  909.    for(count = 1; count <= NumKWMaps; count++) {
  910.       fread(&KeywordMap, sizeof(KWMAPREC), 1, HelpFile);
  911.       printf("Record:%05u   First Keyword:0x%08lX     Leaf Page#:%05u\n", 
  912.              count, KeywordMap.FirstRec, KeywordMap.PageNum);
  913.    }
  914. }
  915.  
  916.  
  917. /***************************************************
  918.   Dumps the Keyword data file
  919. ****************************************************/
  920.  
  921. void KWDataDump(FILE *HelpFile, long FileStart) {
  922.  
  923.    FILEHEADER      FileHdr;
  924.    WORD            NumKWLocs, count;
  925.    long            TopicOffset;
  926.  
  927.    /* Go to the KWDATA file and get the headers */
  928.    fseek(HelpFile, FileStart, SEEK_SET);
  929.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  930.  
  931.    NumKWLocs = FileHdr.FileSize / 4;
  932.    printf("Number of Keyword Occurrances: %5u\n\n", NumKWLocs);
  933.  
  934.    for(count = 1; count <= NumKWLocs; count++) {
  935.       fread(&TopicOffset, sizeof(TopicOffset), 1, HelpFile);
  936.       printf("Occurance:%05u   Topic Offset:0x%08lX\n",
  937.              count, TopicOffset);
  938.    }
  939. }
  940.  
  941.  
  942. /***************************************************
  943.   Dumps the keyword B-Tree file
  944. ****************************************************/
  945.  
  946. void KWBTreeDump(FILE *HelpFile, long FileStart) {
  947.  
  948.    FILEHEADER      FileHdr;
  949.    char            c;
  950.    int             count, Index;
  951.    long            CurrPage, FirstPageLoc;
  952.    BTREEHEADER     BTreeHdr;
  953.    BTREENODEHEADER CurrNode;
  954.    KWBTREEREC      KWBRec;
  955.  
  956.    /* Go to the KWBTREE file and get the headers */
  957.    fseek(HelpFile, FileStart, SEEK_SET);
  958.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  959.    fread(&BTreeHdr, sizeof(BTreeHdr), 1, HelpFile);
  960.  
  961.    /* Save the current location */
  962.    FirstPageLoc = ftell(HelpFile);
  963.    GotoPage(BTreeHdr.RootPage);
  964.  
  965.    printf("# Keywords in |KWBTREE %lu\n\n", BTreeHdr.TotalBtreeEntries);
  966.    CurrPage = OtherGetFirstLeaf(HelpFile, FirstPageLoc, BTreeHdr.NLevels);
  967.  
  968.    do {
  969.        GotoPage(CurrPage);
  970.        fread(&CurrNode, 8, 1, HelpFile);
  971.        for(count = 1; count <= CurrNode.NEntries; count++) {
  972.  
  973.           Index = 0;
  974.           while(c = fgetc(HelpFile))
  975.              KWBRec.Keyword[Index++] = c;
  976.           KWBRec.Keyword[Index] = 0;
  977.  
  978.           fread(&KWBRec.Count, sizeof(int), 1, HelpFile);
  979.           fread(&KWBRec.KWDataOffset, sizeof(long), 1, HelpFile);
  980.          
  981.           printf("KWData Offset:0x%08lX    # Offsets:%05d    Keyword: %s\n", 
  982.                   KWBRec.KWDataOffset, KWBRec.Count, KWBRec.Keyword);
  983.        }
  984.        CurrPage = CurrNode.NextPage;
  985.  
  986.    } while(CurrPage != -1);
  987. }
  988.  
  989.  
  990.  
  991. /***************************************************
  992.   Dumps the Context file.
  993. ****************************************************/
  994.  
  995. void ContextDump(FILE *HelpFile, long FileStart) {
  996.  
  997.    int             count;
  998.    long            CurrPage, FirstPageLoc;
  999.    BTREEHEADER     BTreeHdr;
  1000.    BTREENODEHEADER CurrNode;
  1001.    FILEHEADER      FileHdr;
  1002.    CONTEXTREC      ContextRec;
  1003.  
  1004.    /* Go to the CONTEXT file and get the headers */
  1005.    fseek(HelpFile, FileStart, SEEK_SET);
  1006.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  1007.    fread(&BTreeHdr, sizeof(BTreeHdr), 1, HelpFile);
  1008.  
  1009.    /* Save the current location */
  1010.    FirstPageLoc = ftell(HelpFile);
  1011.    GotoPage(BTreeHdr.RootPage);
  1012.  
  1013.    printf("# Values in hash table:%lu\n\n", BTreeHdr.TotalBtreeEntries);
  1014.  
  1015.    CurrPage = OtherGetFirstLeaf(HelpFile, FirstPageLoc, BTreeHdr.NLevels);
  1016.  
  1017.    do {
  1018.        GotoPage(CurrPage);
  1019.        fread(&CurrNode, 8, 1, HelpFile);
  1020.        for(count = 1; count <= CurrNode.NEntries; count++) {
  1021.  
  1022.           fread(&ContextRec, sizeof(ContextRec), 1, HelpFile);
  1023.          
  1024.           printf("Hash Value:%12ld      Topic Offset:0x%08lX\n", 
  1025.                   ContextRec.HashValue, ContextRec.TopicOffset);
  1026.        }
  1027.        CurrPage = CurrNode.NextPage;
  1028.  
  1029.    } while(CurrPage != -1);
  1030.  
  1031.  
  1032. }
  1033.  
  1034.  
  1035. /***************************************************
  1036.    Dump the Context map file. Real simple.
  1037. ****************************************************/
  1038.  
  1039. void CTXOMAPDump(FILE *HelpFile, long FileStart) {
  1040.  
  1041.    FILEHEADER      FileHdr;
  1042.    CTXOMAPREC      CTXORec;
  1043.    WORD            NumRecs, count;
  1044.  
  1045.    /* Go to the CTXOMAP file and get the headers */
  1046.    fseek(HelpFile, FileStart, SEEK_SET);
  1047.    fread(&FileHdr, sizeof(FileHdr), 1, HelpFile);
  1048.  
  1049.    fread(&NumRecs, sizeof(NumRecs), 1, HelpFile);
  1050.    printf("Number of Context Mapping records: %u\n\n", NumRecs);
  1051.    
  1052.    for (count=1; count <= NumRecs; count++) {
  1053.      fread(&CTXORec, sizeof(CTXORec), 1, HelpFile);
  1054.      printf("Record:%05u    Map ID:0x%08lX     Topic Offset:0x%08lX\n", 
  1055.             count, CTXORec.MapID, CTXORec.TopicOffset);
  1056.    }
  1057. }
  1058.  
  1059.  
  1060.  
  1061. /***************************************************
  1062.   Dumps a given WHIFS file. First thing it does,
  1063.   though, is loads the system rec. This is needed
  1064.   to check if |Phrases or |TOPIC are compressed.
  1065.   After that, it just finds the file and dumps it.
  1066. ****************************************************/
  1067. void DumpFile(FILE *HelpFile) {
  1068.  
  1069.     WORD            FileCounter;
  1070.     char            FileName[20];
  1071.     long            FileOffset, PhrasesOffset;
  1072.  
  1073.  
  1074.     /* Load WHIFSHeader & First Leaf */
  1075.     WHIFSGetFirstLeaf(HelpFile);
  1076.  
  1077.     /* Loads the system record into memory. Need it */
  1078.     /* to determine if compression is used.         */
  1079.  
  1080.     TopicUse = 0;
  1081.     if ((!strcmp(WHIFSFileToRead, "TOPIC")) || (!strcmp(WHIFSFileToRead, "|TOPIC"))) 
  1082.        TopicUse = 1;
  1083.     for (FileCounter = 0; FileCounter < WHIFSHeader.TotalWHIFSEntries; FileCounter++) {
  1084.        GetFile(HelpFile, FileCounter, &FileOffset, FileName);
  1085.        if (!strcmp(FileName, "|SYSTEM"))
  1086.           SysLoad(HelpFile, FileOffset);
  1087.  
  1088.        if ((!strcmp(FileName, "|Phrases")) && (TopicUse))
  1089.           PhrasesOffset = FileOffset;
  1090.     }
  1091.  
  1092.     /* Load Phrases for |TOPIC dump */
  1093.     if (TopicUse) PhraseDump(HelpFile, PhrasesOffset);
  1094.  
  1095.     /* Find the file the user wants to dump */
  1096.  
  1097.     for (FileCounter = 0; FileCounter < WHIFSHeader.TotalWHIFSEntries; FileCounter++) {
  1098.        GetFile(HelpFile, FileCounter, &FileOffset, FileName);
  1099.        if (!strcmp(FileName, WHIFSFileToRead)) break;
  1100.     }
  1101.  
  1102.  
  1103.  
  1104.     if (FileCounter == WHIFSHeader.TotalWHIFSEntries) {
  1105.  
  1106.        /* try appending a | to the filename. */
  1107.        strcpy(FileName, "|");
  1108.        strcat(FileName, WHIFSFileToRead);
  1109.        strcpy(WHIFSFileToRead, FileName);
  1110.  
  1111.        for (FileCounter = 0; FileCounter < WHIFSHeader.TotalWHIFSEntries; FileCounter++) {
  1112.           GetFile(HelpFile, FileCounter, &FileOffset, FileName);
  1113.           if (!strcmp(FileName, WHIFSFileToRead)) break;
  1114.        }
  1115.     }
  1116.  
  1117.  
  1118.     /* Bad file! */
  1119.  
  1120.     if (FileCounter == WHIFSHeader.TotalWHIFSEntries) {
  1121.        printf("File %s not found in help file.\n", WHIFSFileToRead);
  1122.        return;
  1123.     }
  1124.  
  1125.     if (!strcmp(FileName, "|SYSTEM"))
  1126.         SysDump(HelpFile, FileOffset);
  1127.     else if (!strcmp(FileName, "|Phrases"))
  1128.         PhraseDump(HelpFile, FileOffset);
  1129.     else if (!strcmp(FileName, "|FONT"))
  1130.         FontDump(HelpFile, FileOffset);
  1131.     else if (!strcmp(FileName, "|TOMAP"))
  1132.         ToMapDump(HelpFile, FileOffset);
  1133.     else if (!strcmp(FileName, "|TOPIC"))
  1134.     TopicDump(HelpFile, FileOffset);
  1135.     else if (!strcmp(FileName, "|TTLBTREE"))
  1136.         TTLDump(HelpFile, FileOffset); 
  1137.     else if (!strcmp(FileName, "|KWMAP"))
  1138.         KWMapDump(HelpFile, FileOffset);
  1139.     else if (!strcmp(FileName, "|KWDATA"))
  1140.         KWDataDump(HelpFile, FileOffset);
  1141.     else if (!strcmp(FileName, "|KWBTREE"))
  1142.         KWBTreeDump(HelpFile, FileOffset);
  1143.     else if (!strcmp(FileName, "|CONTEXT"))
  1144.         ContextDump(HelpFile, FileOffset); 
  1145.     else if (!strcmp(FileName, "|CTXOMAP"))
  1146.         CTXOMAPDump(HelpFile, FileOffset);
  1147.     else 
  1148.         HexDump(HelpFile, FileOffset);
  1149.  
  1150.  
  1151. }
  1152.  
  1153.  
  1154.  
  1155. /***************************************************
  1156.    Read the Help Header then build the WHIFS B-Tree.
  1157.    Show the WHIFS directory to the user and see what
  1158.    they want to do. (See a file or quit?)
  1159. ****************************************************/
  1160.  
  1161. void HelpDump(FILE *HelpFile) {
  1162.  
  1163.  
  1164.     fread(&HelpHeader, sizeof(HelpHeader), 1, HelpFile);
  1165.     if (HelpHeader.MagicNumber != 0x000000035F3F) {
  1166.     printf("Fatal Error:\n");
  1167.     printf("  Not a valid WinHelp file!\n");
  1168.     return;
  1169.     }
  1170.  
  1171.     if (ReadWHIFSFile) 
  1172.        DumpFile(HelpFile);
  1173.     else
  1174.        ListFiles(HelpFile);
  1175.  
  1176. }
  1177.  
  1178.  
  1179.  
  1180. /***************************************************
  1181.   Show usage.
  1182. ****************************************************/
  1183.  
  1184. void Usage() {
  1185.  
  1186.   printf("Usage:\n");
  1187.   printf(" HELPDUMP helpfile[.hlp] [WHIFSFilename]\n\n\n");
  1188.   printf("   helpfile      - Name of help file (.HLP or .MVB)\n");
  1189.   printf("   WHIFSFilename - Name of internal file to view\n\n");
  1190.   printf(" If only a helpfile is provided, the WHIFS directory is shown.\n");
  1191.   printf(" Provide a WHIFSFilename to display an internal file.\n");
  1192.  
  1193. }
  1194.  
  1195.  
  1196.  
  1197. /***************************************************
  1198.   Open the file and dump it.
  1199. ****************************************************/
  1200.  
  1201. int main(int argc, char *argv[]) {
  1202.  
  1203.     char filename[40];
  1204.     FILE *HelpFile;
  1205.  
  1206.     if (argc < 2) {
  1207.        Usage();
  1208.        return EXIT_FAILURE;
  1209.     }
  1210.     ReadWHIFSFile = 0;
  1211.     if (argc == 3) {
  1212.        strcpy(WHIFSFileToRead, argv[2]);
  1213.        ReadWHIFSFile = 1;
  1214.     }
  1215.  
  1216.     strcpy(filename, argv[1]);
  1217.     strupr(filename);
  1218.     if (!strchr(filename, '.')) 
  1219.        strcat(filename, ".HLP");
  1220.  
  1221.     if ((HelpFile = fopen(filename, "rb")) == NULL) {
  1222.        printf("%s does not exist!", filename);
  1223.        return EXIT_FAILURE;
  1224.     }
  1225.  
  1226.     HelpDump(HelpFile);
  1227.     fclose(HelpFile);
  1228.  
  1229.     return EXIT_SUCCESS;
  1230. }
  1231. 
  1232.